package com.linb.pan.server.modules.file.service.impl;

import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.linb.pan.core.exception.BPanBusinessException;
import com.linb.pan.core.utils.IdUtil;
import com.linb.pan.lock.core.annotation.Lock;
import com.linb.pan.server.common.config.PanServerConfig;
import com.linb.pan.server.modules.file.context.FileChunkSaveContext;
import com.linb.pan.server.modules.file.converter.FileConverter;
import com.linb.pan.server.modules.file.entity.BPanFileChunk;
import com.linb.pan.server.modules.file.enums.MergeFlagEnum;
import com.linb.pan.server.modules.file.mapper.BPanFileChunkMapper;
import com.linb.pan.server.modules.file.service.IFileChunkService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.linb.pan.storage.engine.core.StorageEngine;
import com.linb.pan.storage.engine.core.context.StoreFileChunkContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.Date;

/**
 * <p>
 * 文件分片信息表 服务实现类
 * </p>
 *
 * @author 林云
 * @since 2023-09-02
 */
@Service
public class FileChunkServiceImpl extends ServiceImpl<BPanFileChunkMapper, BPanFileChunk> implements IFileChunkService {

    @Autowired
    private PanServerConfig panServerConfig;

    @Autowired
    private StorageEngine storageEngine;

    @Autowired
    private FileConverter fileConverter;

    /**
     * 文件分片上传
     */
    @Override
    @Lock(name = "saveChunkFileLock", keys = {"#context.userId", "#context.identifier"}, expireSecond = 10L)
    public void saveChunkFile(FileChunkSaveContext context) {
        //1.保存文件分片记录
        doSaveChunkFile(context);
        //2.判断文件分片是否全部上传完成
        doJudgeMergeFile(context);
    }

    /**
     * 委托文件存储引擎保存文件分片并保存文件分片记录
     */
    private void doSaveChunkFile(FileChunkSaveContext context) {
        doStoreFileChunk(context);
        doSaveRecord(context);
    }

    /**
     * 判断文件分片是否全部上传完成
     * 如果完成就设置状态为需要分片上传
     */
    private void doJudgeMergeFile(FileChunkSaveContext context) {
        QueryWrapper queryWrapper = Wrappers.query();
        queryWrapper.eq("identifier", context.getIdentifier());
        queryWrapper.eq("create_user", context.getUserId());
        int count = count(queryWrapper);
        if (count == context.getTotalChunks().intValue()) {
            context.setMergeFlagEnum(MergeFlagEnum.READY);
        }
    }

    /**
     * 保存文件分片记录
     */
    private void doSaveRecord(FileChunkSaveContext context) {
        BPanFileChunk fileChunk = new BPanFileChunk();
        fileChunk.setId(IdUtil.get());
        fileChunk.setIdentifier(context.getIdentifier());
        fileChunk.setRealPath(context.getRealPath());
        fileChunk.setChunkNumber(context.getChunkNumber());
        fileChunk.setExpirationTime(DateUtil.offsetDay(new Date(), panServerConfig.getChunkFileExpirationDays()));
        fileChunk.setCreateUser(context.getUserId());
        fileChunk.setCreateTime(new Date());
        if (!save(fileChunk)) {
            throw new BPanBusinessException("文件分片上传失败");
        }
    }

    /**
     * 委托文件存储引擎保存文件分片
     */
    private void doStoreFileChunk(FileChunkSaveContext context) {
        try {
            StoreFileChunkContext storeFileChunkContext = fileConverter.fileChunkSaveContextToStoreFileChunkContext(context);
            storeFileChunkContext.setInputStream(context.getFile().getInputStream());
            storageEngine.storeChunk(storeFileChunkContext);
            context.setRealPath(storeFileChunkContext.getRealPath());
        } catch (IOException e) {
            e.printStackTrace();
            throw new BPanBusinessException("文件分片上传失败");
        }
    }

}
